﻿using mozhi.smarterp.Business.Enums;
using mozhi.smarterp.Business.Inventories.Services;
using mozhi.smarterp.Business.Products.Repositories;
using mozhi.smarterp.Business.Purchases.Repositories;
using Mozhi.Abp.Domain.AuthorCenter;
using Mozhi.Abp.Domain.BasicData;
using Mozhi.Abp.Domain.BasicData.Repositories;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Guids;

namespace mozhi.smarterp.Business.Purchases.Services
{
    public class PurchaseReceiptManager : DocumentManagerBase<PurchaseReceipt, Guid>
    {
        private IGuidGenerator _guidGenerator;
        private IDepartmentRepository _departmentRepository;
        private ICurrencyRepository _currencyRepository;
        private IEmployeeRepository _employeeRepository;
        private IProductInventorySettingRepository _inventorySettingRepository;
        private IProductRepository _productRepository;
        private IWarehouseRepository _warehouseRepository;
        private ITaxRateRepository _taxRateRepository;
        private IOrganizationRepository _organizationRepository;
        private InventoryTransationManager _inventoryTransationManager;
        private IPurchaseReceiptPayableRepository _purchaseReceiptPayableRepository;
        private IProductAuxiliaryUnitRepository _auxiliaryUnitRepository;
        private IUnitRepository _unitRepository;
        private IPurchaseOrderReceiptRepository _orderReceiptRepository;


        public PurchaseReceiptManager(IDepartmentRepository departmentRepository, ICurrencyRepository currencyRepository, IEmployeeRepository employeeRepository, IGuidGenerator guidGenerator,
            IProductInventorySettingRepository inventorySettingRepository, IProductRepository productRepository, IWarehouseRepository warehouseRepository, ITaxRateRepository taxRateRepository,
            IOrganizationRepository organizationRepository, IPurchaseReceiptPayableRepository purchaseReceiptPayableRepository, IProductAuxiliaryUnitRepository auxiliaryUnitRepository, IUnitRepository unitRepository,
            IPurchaseReceiptRepository repository, INumberGeneratorRepository numberGeneratorRepository, IPurchaseOrderReceiptRepository orderReceiptRepository)
            : base(repository, numberGeneratorRepository, DocumentType.PurchaseReceipt)
        {
            _departmentRepository = departmentRepository;
            _currencyRepository = currencyRepository;
            _employeeRepository = employeeRepository;
            _guidGenerator = guidGenerator;
            _inventorySettingRepository = inventorySettingRepository;
            _productRepository = productRepository;
            _warehouseRepository = warehouseRepository;
            _taxRateRepository = taxRateRepository;
            _organizationRepository = organizationRepository;
            _purchaseReceiptPayableRepository = purchaseReceiptPayableRepository;
            _auxiliaryUnitRepository = auxiliaryUnitRepository;
            _unitRepository = unitRepository;
            _orderReceiptRepository = orderReceiptRepository;
        }

        public async Task<PurchaseReceipt> GetAsync(Guid? id, Guid? departmentId, Guid? warehouseClerkId, DateTime date, string comment,
            Guid? paymentTermId, Guid currencyId, decimal exchangeRate, bool includeTax, Guid purchaseDepartmentId, Guid purchaserId, Guid supplierId)
        {
            var department = departmentId.HasValue ? await _departmentRepository.GetAsync(departmentId.Value) : null;

            var currency = await _currencyRepository.GetAsync(currencyId);

            var warehouseClerk = warehouseClerkId.HasValue ? await _employeeRepository.GetAsync(warehouseClerkId.Value) : null;

            PurchaseReceipt purchaseReceipt;

            if (id.HasValue)
            {
                purchaseReceipt = await _repository.GetAsync(id.Value);

                if (purchaseReceipt == null)
                    throw new UserFriendlyException($"采购入库单：{id}不存在", "100000");

                purchaseReceipt.Change(date, includeTax, currency, exchangeRate, false, department, warehouseClerk);
            }
            else
            {
                var number = await GetNextNumberAsync();
                purchaseReceipt = new PurchaseReceipt(_guidGenerator.Create(), number, date, includeTax, currency, exchangeRate, false, department, warehouseClerk, supplierId, purchaseDepartmentId, purchaserId, paymentTermId, comment);
            }

            return purchaseReceipt;

        }

        public async Task<PurchaseReceiptItem> GetItemAsync(Guid? id, Guid productId, Guid warehouseId, Guid? locationId, Guid purchaseUnitId, decimal purchaseQuantity, decimal receiptQuantity,
            decimal basicQuantity, decimal price, Guid? taxRateId,
            string batchNumber, string serialNumber, DateTime? productionDate, bool isGift, string remark)
        {

            var inventorySetting = await _inventorySettingRepository.GetByProductIdAsync(productId);

            if (inventorySetting == null)
                throw new UserFriendlyException("产品未设置库存信息", "100000");

            inventorySetting.ValidateProductInventory(basicQuantity, batchNumber, serialNumber, productionDate);

            var product = await _productRepository.GetAsync(productId);

            var warehouse = await _warehouseRepository.GetAsync(warehouseId);

            var taxRate = taxRateId == null ? null : await _taxRateRepository.GetAsync(taxRateId.Value);

            var auxiliaryUnit = product.BasicUnitId == purchaseUnitId ? null : await _auxiliaryUnitRepository.GetAsync(productId, purchaseUnitId);

            var purchaseUnit = await _unitRepository.GetAsync(purchaseUnitId);

            id = id ?? _guidGenerator.Create();

            PurchaseReceiptItem purchaseReceiptItem = new PurchaseReceiptItem(id.Value, product, warehouse, locationId, auxiliaryUnit, purchaseUnit, purchaseQuantity, receiptQuantity, basicQuantity, price, taxRate,
                batchNumber, serialNumber, productionDate, inventorySetting.ShelfLifeDays, isGift, remark);

            return purchaseReceiptItem;

        }

        public async Task<PurchaseReceipt> UpdateStatusAsync(Guid id, DocumentStatusType status)
        {
            var receipt = await _repository.GetAsync(id, false);

            if (receipt == null)
                throw new UserFriendlyException($"采购入库单：{id}不存在", "100000");

            //审核的时候要校验单据单据日期
            if (status == DocumentStatusType.Approved || status == DocumentStatusType.PendingApproval)
                await ValidateDocumentDateAsync(receipt.Date);

            receipt.SetStatus(status);

            //撤销审核要满足几个条件 1：没有下推应付款时，可用库存数量是要足够的，2：下推了应付款，需要先删除应付款单
            if (receipt.Status == DocumentStatusType.Approved && status == DocumentStatusType.PendingApproval)
            {
                var receiptPayableCount = await _purchaseReceiptPayableRepository.GetCountAsync(receipt.Id);

                if (receiptPayableCount > 0)
                    throw new UserFriendlyException($"采购入库单：{receipt.Number}已关联应付单，不允许反审核", "100000");

                await HandleUnApproveAsync(receipt);

            }
            else if (receipt.Status == DocumentStatusType.PendingApproval && status == DocumentStatusType.Approved)
            {
                await HandleApproveAsync(receipt);
            }

            return receipt;
        }

        public async Task DeleteAsync(Guid id)
        {
            var receipt = await _repository.GetAsync(id);

            if (receipt == null)
                throw new UserFriendlyException($"采购入库单：{id}不存在", "100000");


            if (receipt.Status != DocumentStatusType.Creation)
                throw new UserFriendlyException($"采购入库单：{receipt.Number}非创建状态，不允许删除", "100000");

            var orderReceipts = await _orderReceiptRepository.GetListAsync(null, null, receipt.Id, null);

            await _repository.DeleteAsync(id);

            if (orderReceipts.Count > 0)
            {
                foreach (var orderReceipt in orderReceipts)
                {
                    await _orderReceiptRepository.DeleteAsync(orderReceipt.Id);
                }
            }

        }

        #region
        private async Task ValidateDocumentDateAsync(DateTime date)
        {
            var organization = await _organizationRepository.GetAsync();

            if (organization == null)
                throw new BusinessException("无法获取到组织信息", "100000");

            organization.ValidateDocumentDate(date);
        }

        private async Task HandleApproveAsync(PurchaseReceipt receipt)
        {
            var items = receipt.Items;

            if (items == null || items.Count == 0)
                throw new UserFriendlyException($"采购入库单{receipt.Number}为没有分录", "100000");

            foreach (var item in items)
            {
                var inventoryFlowType = receipt.IsCreditNote ? InventoryFlowType.Outbound : InventoryFlowType.Inbound;

                var transaction = await _inventoryTransationManager.GetAsync(inventoryFlowType, DocumentType.PurchaseReceipt, receipt.Id, item.Id, receipt.Date,
                    item.ProductId, item.WarehouseId, item.LocationId, item.BatchNumber, item.SerialNumber, item.ProductionDate, item.ExpirationDate, item.BasicQuantity, true);

                await _inventoryTransationManager.CalculateInventoryRealTimesAsync(transaction);
            }
        }

        private async Task HandleUnApproveAsync(PurchaseReceipt receipt)
        {
            var items = receipt.Items;

            if (items == null || items.Count == 0)
                throw new UserFriendlyException($"采购入库单{receipt.Number}为没有分录", "100000");

            foreach (var item in items)
            {
                await _inventoryTransationManager.DeleteAsync(receipt.Id, item.Id, true);
            }
        }

        #endregion
    }
}
